<script lang="ts" module>
    export async function downloadFile(url: string, name = "unnamed") {
        try {
            const res = await fetch(url);
            const blob = await res.blob();

            if (!blob) return Promise.reject();

            const localUrl = URL.createObjectURL(blob);
            const a = document.createElement("a");
            a.setAttribute("href", localUrl);
            a.setAttribute("download", name);
            a.click();
            URL.revokeObjectURL(localUrl);
            return Promise.resolve();
        } catch (e) {
            return Promise.reject(e);
        }
    }
</script>

<script lang="ts">
    import { onDestroy, onMount } from "svelte";
    import usezIndex from "../utils/usezIndex";
    import type { ImagePreviewProps } from "./ImagePreview";
    import { useStyle } from "../utils/useStyle";
    import Portal from "svelte-portal";
    import usePortal from "../utils/usePortal";
    import { Spin } from "../Spin";
    import { Space } from "../Space";
    import Icon from "../Common/Icons/Icon.svelte";

    let { visible = $bindable(false), ...props }: ImagePreviewProps = $props();
    const zindex = usezIndex();
    const store = $state({
        transition: true,
        original: false,
        translate: { x: 0, y: 0 },
        currentIndex: props.initIndex || 0,
        scale: 1,
        degree: 0,
        startX: 0,
        startY: 0,
        prevOverflow: "", // prevent body scrolling
        status: "loading", // image status
        downloading: false,
    });

    const maskClosable = props.maskClosable ?? true;
    const infinite = props.infinite ?? true;

    const handleClickMask = (e: any) => {
        e.preventDefault && e.preventDefault();
        e.stopPropagation && e.stopPropagation();

        if (maskClosable) {
            handleClose(e);
        }
    };

    // 显示的时候如果有默认index则重新设置currentIndex
    $effect(() => {
        const v = visible;
        if (v) {
            store.currentIndex = props.initIndex || 0;
            resetStyle();
            store.original = false;
        }
    });

    $effect(() => {
        store.currentIndex;
        store.status = "loading";
    });

    const handleMousedown = (e: any) => {
        e.preventDefault && e.preventDefault();
        e.stopPropagation && e.stopPropagation();

        const { pageX, pageY, which } = e;
        if (which !== 1) return;
        store.startX = pageX;
        store.startY = pageY;
        store.transition = false;
        document.addEventListener("mousemove", handleMousemove);
        document.addEventListener("mouseup", handleMouseup);
    };

    const handleMousemove = (event: any) => {
        event.stopPropagation();
        const { pageX, pageY } = event;

        const x = store.translate.x + (pageX - store.startX);
        const y = store.translate.y + (pageY - store.startY);

        store.translate = { x, y };
        store.startX = pageX;
        store.startY = pageY;
    };

    const handleMouseup = () => {
        store.transition = true;
        document.removeEventListener("mousemove", handleMousemove);
        document.removeEventListener("mouseup", handleMouseup);
    };

    const handleKeydown = (e: any) => {
        if (!visible) return;
        const { keyCode } = e;
        if (keyCode === 37) handleSwitch(false);
        if (keyCode === 39) handleSwitch(true);
        if (keyCode === 38) handleOperation(e, "zoomIn");
        if (keyCode === 40) handleOperation(e, "zoomOut");
        if (keyCode === 32) {
            e.preventDefault && e.preventDefault();
            store.original = !store.original;
        }
    };

    const handleKeyup = (e: any) => {
        if (!visible) return;
        const { keyCode } = e;
        if (keyCode === 27) handleClose(e);
    };

    const handleWheel = (e: any) => {
        if (!visible) return;
        e.preventDefault && e.preventDefault();
        e.stopPropagation && e.stopPropagation();
        e.stopImmediatePropagation && e.stopImmediatePropagation();
        handleOperation(e, e.deltaY < 0 ? "zoomIn" : "zoomOut");
        return false;
    };

    onMount(() => {
        document.addEventListener("wheel", handleWheel, { passive: false });
        document.addEventListener("keydown", handleKeydown);
        document.addEventListener("keyup", handleKeyup);
    });
    onDestroy(() => {
        document.removeEventListener("mousemove", handleMousemove);
        document.removeEventListener("mouseup", handleMouseup);
        document.removeEventListener("wheel", handleWheel);
        document.removeEventListener("keydown", handleKeydown);
        document.removeEventListener("keyup", handleKeyup);
    });

    const handleImageLoad = () => {
        store.status = "loaded";
    };

    const handleImageError = () => {
        store.status = "failed";
    };

    const handleOperation = (e: any, op: string) => {
        e.stopPropagation && e.stopPropagation();

        if (op === "zoomIn" && store.scale < 6) {
            store.scale = store.scale + 0.25;
        }
        if (op === "zoomOut" && store.scale > 0.25) {
            store.scale = store.scale - 0.25;
        }
        if (op === "rotateLeft") {
            store.degree = store.degree - 90;
        }
        if (op === "rotateRight") {
            store.degree = store.degree + 90;
        }
        if (op === "original") {
            store.original = !store.original;
            store.transition = false;
            resetStyle();
            setTimeout(() => {
                store.transition = true;
            }, 0);
        }
        if (op === "download") {
            store.downloading = true;
            downloadFile(props.previewList[store.currentIndex])
                .then(() => {
                    store.downloading = false;
                })
                .catch(() => {
                    store.downloading = false;
                });
        }
    };

    const resetStyle = () => {
        store.scale = 1;
        store.degree = 0;
        store.translate = { x: 0, y: 0 };
    };

    // 左右切换
    const handleSwitch = (next: boolean) => {
        if (next) {
            if (store.currentIndex + 1 === props.previewList.length) {
                if (infinite) {
                    resetStyle();
                    store.currentIndex = 0;
                }
            } else {
                resetStyle();
                store.currentIndex = store.currentIndex + 1;
            }
        } else {
            if (store.currentIndex === 0) {
                if (infinite) {
                    resetStyle();
                    store.currentIndex = props.previewList.length - 1;
                }
            } else {
                resetStyle();
                store.currentIndex = store.currentIndex - 1;
            }
        }
        props.onswitch?.(store.currentIndex);
    };

    const handleClose = (e: any) => {
        visible = false;
        e.stopPropagation && e.stopPropagation();
        props.onclose?.();
    };

    const imgClasses = $derived(
        useStyle(
            {},
            {
                "cm-image-preview-image": true,
                "cm-image-preview-grabbing": !store.transition,
                "cm-image-preview-hidden": store.status === "failed",
                "cm-image-preview-transition": store.transition,
                "cm-image-preview-limit": !store.original,
            },
        ),
    );

    const imageStyle = $derived.by(() => {
        let translateX = store.translate.x / store.scale;
        let translateY = store.translate.y / store.scale;
        const mod = store.degree % 360;
        if ([90, -270].includes(mod)) {
            [translateX, translateY] = [translateY, -translateX];
        }
        if ([180, -180].includes(mod)) {
            [translateX, translateY] = [-translateX, -translateY];
        }
        if ([270, -90].includes(mod)) {
            [translateX, translateY] = [-translateY, translateX];
        }
        return `transform: 
                scale(${store.scale})
                rotate(${store.degree}deg)
                translate(${translateX}px, ${translateY}px)`;
    });

    const hasLeftSwitchEnd = () => {
        return infinite ? false : store.currentIndex === 0;
    };
    const hasRightSwitchEnd = () => {
        const len = props.previewList.length;
        return infinite ? false : store.currentIndex >= len - 1;
    };

    const leftClasses = $derived({ "cm-image-preview-arrow-left": true, "cm-image-preview-arrow-disabled": hasLeftSwitchEnd() });
    const rightClasses = $derived({ "cm-image-preview-arrow-right": true, "cm-image-preview-arrow-disabled": hasRightSwitchEnd() });

    const currentSrc = $derived(props.previewList[store.currentIndex]);

    const stop = (e: any) => {
        e.stopPropagation && e.stopPropagation();
        e.preventDefault && e.preventDefault();
    };

    const id = "cm-image-preview-portal";
</script>

<Portal target={usePortal(id, id)}>
    {#if visible}
        <div class="cm-image-preview-mask" style={`z-index: ${zindex - 1}`}></div>
        <div class="cm-image-preview-wrap" style={`z-index: ${zindex}`}>
            <!-- svelte-ignore a11y_click_events_have_key_events -->
            <!-- svelte-ignore a11y_no_static_element_interactions -->
            <div class="cm-image-preview" onclick={handleClickMask}>
                {#if store.status === "loading"}
                    <Spin class="cm-image-preview-loading" type="dot" size="large" />
                {/if}
                {#if store.status === "failed"}
                    <div class="cm-image-preview-fail">
                        <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"
                            ><path
                                d="M948.622222 173.511111L506.311111 113.777778l-118.044444 133.688889 135.111111 251.733333L412.444444 750.933333l9.955556 99.555556-22.755556-99.555556 12.8-228.977777-193.422222-263.111112L307.2 113.777778 66.844444 180.622222c-25.6 7.111111-42.666667 32.711111-38.4 59.733334l95.288889 664.177777c4.266667 29.866667 31.288889 49.777778 61.155556 45.511111l237.511111-35.555555L851.911111 952.888889c28.444444 2.844444 54.044444-18.488889 58.311111-46.933333l85.333334-672.711112c4.266667-29.866667-17.066667-56.888889-46.933334-59.733333z m-164.977778 93.866667c35.555556 0 65.422222 29.866667 65.422223 65.422222S819.2 398.222222 783.644444 398.222222s-65.422222-29.866667-65.422222-65.422222 29.866667-65.422222 65.422222-65.422222z m88.177778 526.222222c-1.422222 11.377778-11.377778 21.333333-24.177778 19.911111l-304.355555-27.022222c-11.377778-1.422222-21.333333-11.377778-19.911111-24.177778 1.422222-11.377778 11.377778-21.333333 24.177778-19.911111l304.355555 27.022222c11.377778 1.422222 19.911111 11.377778 19.911111 24.177778z"
                                fill="#BCC3C9"
                            /></svg
                        >
                    </div>
                {/if}
                <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
                <!-- svelte-ignore a11y_missing_attribute -->
                <img class={imgClasses} style={imageStyle} src={currentSrc} onmousedown={handleMousedown} onload={handleImageLoad} onerror={handleImageError} onclick={stop} />
                <Space dir="h" class="cm-image-preview-operations" size={0}>
                    <span>
                        <svg
                            class="cm-image-preview-operations-item"
                            onclick={(e: any) => handleOperation(e, "zoomIn")}
                            viewBox="0 0 1024 1024"
                            version="1.1"
                            xmlns="http://www.w3.org/2000/svg"
                            width="200"
                            height="200"
                            ><path
                                d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
                                fill="#ffffff"
                            /><path
                                d="M921 867L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
                                fill="#ffffff"
                            /></svg
                        >
                    </span>
                    <span>
                        <svg
                            class="cm-image-preview-operations-item"
                            onclick={(e: any) => handleOperation(e, "zoomOut")}
                            viewBox="0 0 1024 1024"
                            version="1.1"
                            xmlns="http://www.w3.org/2000/svg"
                            width="200"
                            height="200"
                            ><path d="M637 443H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h312c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z" fill="#ffffff" /><path
                                d="M921 867L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
                                fill="#ffffff"
                            /></svg
                        >
                    </span>
                    <span>
                        {#if store.original}
                            <svg
                                class="cm-image-preview-operations-item"
                                onclick={(e: any) => handleOperation(e, "original")}
                                viewBox="0 0 1024 1024"
                                version="1.1"
                                xmlns="http://www.w3.org/2000/svg"
                                width="200"
                                height="200"
                                ><path
                                    d="M864 128H160c-19.2 0-32 12.8-32 32v704c0 19.2 12.8 32 32 32h704c19.2 0 32-12.8 32-32V160c0-19.2-12.8-32-32-32z m-32 704H192V192h640v640z"
                                    fill="#ffffff"
                                /><path
                                    d="M320 384v288c0 19.2 12.8 32 32 32s32-12.8 32-32V352c0-19.2-12.8-32-32-32h-32c-19.2 0-32 12.8-32 32s12.8 32 32 32zM640 384v288c0 19.2 12.8 32 32 32s32-12.8 32-32V352c0-19.2-12.8-32-32-32h-32c-19.2 0-32 12.8-32 32s12.8 32 32 32z"
                                    fill="#ffffff"
                                /><path d="M512 384m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z" fill="#ffffff" /><path d="M512 640m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z" fill="#ffffff" /></svg
                            >
                        {:else}
                            <svg
                                class="cm-image-preview-operations-item"
                                onclick={(e: any) => handleOperation(e, "original")}
                                viewBox="0 0 1024 1024"
                                version="1.1"
                                xmlns="http://www.w3.org/2000/svg"
                                width="200"
                                height="200"
                                ><path
                                    d="M358.058667 128H156.970667A28.970667 28.970667 0 0 0 128 157.013333v202.837334c0 7.978667 6.528 14.506667 14.506667 14.506666h43.434666a14.506667 14.506667 0 0 0 14.506667-14.506666V200.448h157.610667a14.506667 14.506667 0 0 0 14.506666-14.506667V142.506667a14.506667 14.506667 0 0 0-14.506666-14.506667zM881.493333 649.642667h-43.434666a14.506667 14.506667 0 0 0-14.506667 14.506666v159.402667h-157.610667a14.506667 14.506667 0 0 0-14.506666 14.506667v43.434666c0 7.978667 6.570667 14.506667 14.506666 14.506667h201.088c16 0 28.970667-12.928 28.970667-29.013333v-202.837334a14.506667 14.506667 0 0 0-14.506667-14.506666zM358.058667 823.552H200.448v-159.402667a14.506667 14.506667 0 0 0-14.506667-14.506666H142.506667a14.506667 14.506667 0 0 0-14.506667 14.506666v202.88c0 16 12.970667 28.970667 29.013333 28.970667h201.045334a14.506667 14.506667 0 0 0 14.506666-14.506667v-43.434666a14.506667 14.506667 0 0 0-14.506666-14.506667zM866.986667 128h-201.088a14.506667 14.506667 0 0 0-14.506667 14.506667v43.434666c0 7.978667 6.570667 14.506667 14.506667 14.506667h157.610666v159.402667c0 7.978667 6.528 14.506667 14.506667 14.506666h43.434667a14.506667 14.506667 0 0 0 14.506666-14.506666V156.970667A28.928 28.928 0 0 0 866.986667 128z"
                                    fill="#ffffff"
                                /></svg
                            >
                        {/if}
                    </span>
                    <span>
                        <svg
                            class="cm-image-preview-operations-item"
                            onclick={(e: any) => handleOperation(e, "rotateLeft")}
                            viewBox="0 0 1024 1024"
                            version="1.1"
                            xmlns="http://www.w3.org/2000/svg"
                            width="200"
                            height="200"
                            ><path
                                d="M672 418H144c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H188V494h440v326z m191.3-491.5c-78.8-100.7-196-153.6-314.6-154.2l-0.2-64c0-6.5-7.6-10.1-12.6-6.1l-128 101c-4 3.1-3.9 9.1 0 12.3L492 318.6c5.1 4 12.7 0.4 12.6-6.1v-63.9c12.9 0.1 25.9 0.9 38.8 2.5 42.1 5.2 82.1 18.2 119 38.7 38.1 21.2 71.2 49.7 98.4 84.3 27.1 34.7 46.7 73.7 58.1 115.8 11 40.7 14 82.7 8.9 124.8-0.7 5.4-1.4 10.8-2.4 16.1h74.9c14.8-103.6-11.3-213-81-302.3z"
                                fill="#ffffff"
                            /></svg
                        >
                    </span>
                    <span>
                        <svg
                            class="cm-image-preview-operations-item"
                            onclick={(e: any) => handleOperation(e, "rotateRight")}
                            viewBox="0 0 1024 1024"
                            version="1.1"
                            xmlns="http://www.w3.org/2000/svg"
                            width="200"
                            height="200"
                            ><path
                                d="M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-0.4-12.6 6.1l-0.2 64c-118.6 0.5-235.8 53.4-314.6 154.2-69.6 89.2-95.7 198.6-81.1 302.4h74.9c-0.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8zM880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H396V494h440v326z"
                                fill="#ffffff"
                            /></svg
                        >
                    </span>
                    <span>
                        {#if store.downloading}
                            <svg
                                class="cm-image-preview-operations-item ivu-image-preview-operations-wait ivu-anim-loop"
                                viewBox="0 0 1024 1024"
                                version="1.1"
                                xmlns="http://www.w3.org/2000/svg"
                                width="200"
                                height="200"
                                ><path d="M512 64c247.2 0 448 200.8 448 448h-64c0-212-172-384-384-384V64z m0 832c-212 0-384-172-384-384H64c0 247.2 200.8 448 448 448v-64z" fill="#ffffff" /></svg
                            >
                        {:else}
                            <svg
                                class="cm-image-preview-operations-item"
                                onclick={(e: any) => handleOperation(e, "download")}
                                viewBox="0 0 1024 1024"
                                version="1.1"
                                xmlns="http://www.w3.org/2000/svg"
                                width="200"
                                height="200"
                                ><path
                                    d="M505.7 621c3.2 4.1 9.4 4.1 12.6 0l112-141.7c4.1-5.2 0.4-12.9-6.3-12.9h-72.1V120c0-4.4-3.6-8-8-8h-64c-4.4 0-8 3.6-8 8v346.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8z"
                                    fill="#ffffff"
                                /><path
                                    d="M903 516h-64c-4.4 0-8 3.6-8 8v300c0 4.4-3.6 8-8 8H199c-4.4 0-8-3.6-8-8V524c0-4.4-3.6-8-8-8h-64c-4.4 0-8 3.6-8 8v372c0 8.8 7.2 16 16 16h768c8.8 0 16-7.2 16-16V524c0-4.4-3.6-8-8-8z"
                                    fill="#ffffff"
                                /></svg
                            >
                        {/if}
                    </span>
                </Space>
                {#if props.previewList.length > 1}
                    <Icon
                        icon="chevronLeft"
                        class={leftClasses}
                        onclick={(e) => {
                            stop(e);
                            handleSwitch(false);
                        }}
                    />
                    <Icon
                        icon="chevronRight"
                        class={rightClasses}
                        onclick={(e) => {
                            stop(e);
                            handleSwitch(true);
                        }}
                    />
                {/if}
                <Icon icon="times" class="cm-image-preview-arrow-close" onclick={handleClose} />
            </div>
        </div>
    {/if}
</Portal>
